

#include <asset/assetsystem.h>
#include <asset/reader/assetreadertexture.h>
#include <fs/fileinfo.h>
#include <gfx/image.h>
#include <gfx/itexture.h>
#include <cffformat.h>
#include <vfs.h>
#include <stdio.h>



ceAssetReaderTexture::ceAssetReaderTexture()
{
	CE_OBJECT_CONSTR
}


bool ceAssetReaderTexture::CanRead(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
	if (type != iTexture::AssetTypeID)
		{
			return false;
		}

	ceFileInfo info (locator.GetLocation());
	std::string ext = info.GetExtension();
	return ext == "tex";
}


cffNode* ceAssetReaderTexture::FindNode(cffNode *parent, const char *name)
{
	for (unsigned i=0, j=parent->GetNumberOfNodes(); i<j; ++i)
		{
			cffNode* textureNode = parent->GetNode(i);

			if (!textureNode || !textureNode->HasAttribute(0))
				{
					continue;
				}

			if (textureNode->GetName() == std::string("texture2d")
					&& std::string (name) == (const char*)textureNode->GetAttribute(0))
				{
					return textureNode;
				}

			if (textureNode->GetName() == std::string("textureCube")
					&& std::string (name) == (const char*)textureNode->GetAttribute(0))
				{
					return textureNode;
				}
		}

	return 0;
}

ceRef<ceAsset> ceAssetReaderTexture::Read(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
  ceReleasePtr<iFile> file (ceVFS::Get()->Open(locator.GetLocation()));
	if (!file)
		{
			return 0;
		}
	long size = file->GetLength();

	char* buffer = new char[size+1];
	file->Read(buffer, size);
	file->Close ();

	buffer[size] = '\0';

	cffParser parser;
  ceReleasePtr<cffNode> parent (parser.ParseFromBuffer(buffer));
  delete [] buffer;
	if (!parent)
		{
			return 0;
		}

	
	cffNode* textureNode = FindNode(parent, locator.GetName());
	if (!textureNode)
		{
			return 0;
		}

	iDevice* device = engine->GetDevice();

	ceTextureDescriptor desc;
	desc.anisotropic  = device->GetDefaultAnisotropic();
	desc.mipMap       = device->IsDefaultMipMap();
	desc.filter       = device->GetDefaultFilter();
	desc.wrapMode     = device->GetDefaultWrapMode();
	desc.format       = PF_R8G8B8;
	bool formatByImage = true;

	cffNode* formatNode = textureNode->GetNode("format");
	if (formatNode && formatNode->HasAttribute(0))
		{
			std::string formatText (formatNode->GetAttribute(0));
			if (std::string ("R8G8B8") == formatText)
				{
					desc.format = PF_R8G8B8;
					formatByImage = false;
				}
			else if (std::string("R8G8B8A8") == formatText)
				{
					desc.format = PF_R8G8B8A8;
					formatByImage = false;
				}
		}


	cffNode* filterNode = textureNode->GetNode("filter");
	if (filterNode && textureNode->HasAttribute(0))
		{
			std::string filterText (filterNode->GetAttribute(0));
			if (std::string ("nearest") == filterText)
				{
					desc.filter = F_Nearest;
				}
			else if (std::string ("bilinear") == filterText)
				{
					desc.filter = F_Bilinear;
				}
			else if (std::string ("trilinear") == filterText)
				{
					desc.filter = F_Trilinear;
				}
			else if (std::string ("anisotropic") == filterText)
				{
					desc.filter = F_Anisotropic;
				}
		}


	cffNode* mipMapNode = textureNode->GetNode("mipmap");
	if (mipMapNode)
		{
			desc.mipMap = true;
			if (mipMapNode->HasAttribute(0))
				{
					std::string mipMapText (mipMapNode->GetAttribute(0));
					if (std::string("false") == mipMapText)
						{
							desc.mipMap = false;
						}
				}
		}

	cffNode* anisotropicNode = textureNode->GetNode("anisotropy");
	if (anisotropicNode && anisotropicNode->HasAttribute(0))
		{
			desc.anisotropic = anisotropicNode->GetAttribute(0).AsInt();
		}


	cffNode* wrapNode = textureNode->GetNode("wrap");
	if (wrapNode && wrapNode->HasAttribute(0))
		{
			std::string wrapText (wrapNode->GetAttribute(0));
			if (std::string ("repeat") == wrapText)
				{
					desc.wrapMode = WM_Repeat;
				}
			else if (std::string ("clamp") == wrapText)
				{
					desc.wrapMode = WM_Clamp;
				}
			else if (std::string ("clamp_to_edge") == wrapText)
				{
					desc.wrapMode = WM_ClampToEdge;
				}
			else if (std::string ("clamp_to_border") == wrapText)
				{
					desc.wrapMode = WM_ClampToBorder;
				}
		}


	cffNode* filenameNode = textureNode->GetNode("filename");
	cffNode* dimensionNode = textureNode->GetNode("dimension");
	if (!filenameNode && !dimensionNode)
		{
			printf ("Neither filename nor dimension given for texture. Aborting\n");
			fflush(stdout);
			return 0;
		}
	if (filenameNode && dimensionNode)
		{
			printf ("Filename and dimension given for texture. Not sure what element to use. So aborting\n");
			fflush(stdout);
			return 0;
		}

	iTexture* resultTexture = 0;
	if (textureNode->GetName() == std::string("texture2d"))
		{

			iTexture2D* texture = 0;
			if (dimensionNode)
				{
					if (formatByImage)
						{
							printf ("No image and no format given\n");
							fflush(stdout);
							return 0;
						}
					if (!dimensionNode->HasAttribute(0) || !dimensionNode->HasAttribute(1))
						{
							printf ("no dimension present for texture\n");
							fflush(stdout);
							return 0;
						}
					int width  = dimensionNode->GetAttribute(0).AsInt();
					int height = dimensionNode->GetAttribute(1).AsInt();

					desc.size = ceRect (width, height);

					texture = device->CreateTexture2D(desc);
				}
			else if (filenameNode)
				{
					if (!filenameNode->HasAttribute(0))
						{
							printf ("No name for texture given\n");
              fflush(stdout);
              return 0;
						}
					std::string name (filenameNode->GetAttribute(0));
					std::string location = "";
					if (filenameNode->HasAttribute(1))
						{
							location = filenameNode->GetAttribute(1);
						}

					ceAssetLocator loc (name.c_str(), location.length() ? location.c_str() : 0);
					ceImage* image = engine->GetAssetSystem()->GetAsset<ceImage>(loc);
					if (!image)
						{
							printf ("image '%s' not found\n", name.c_str ());
              fflush(stdout);
							return 0;
						}

					desc.size = ceRect (image->GetWidth(), image->GetHeight());
					if (formatByImage)
						{
							desc.format = image->GetFormat();
						}

					texture = device->CreateTexture2D(desc);
					texture->SetData(image->GetFormat(), image->GetData());
				}
			else
				{
					assert (false);
				}
      texture->SetLocator(locator);

			resultTexture = texture;
		}


	return resultTexture;
}

